const axios = require('axios');

// 安全测试配置
const BASE_URL = 'http://localhost:3000/api';
let authToken = '';

// 颜色输出函数
const colors = {
  green: '\x1b[32m',
  red: '\x1b[31m',
  yellow: '\x1b[33m',
  blue: '\x1b[34m',
  reset: '\x1b[0m'
};

function log(message, color = 'reset') {
  console.log(`${colors[color]}${message}${colors.reset}`);
}

// 测试工具函数
async function testSecurity(name, testFunction) {
  try {
    log(`\n${'='.repeat(60)}`, 'blue');
    log(`安全测试: ${name}`, 'blue');
    log(`${'='.repeat(60)}`, 'blue');
    
    await testFunction();
    log(`✅ ${name} - 安全测试通过`, 'green');
  } catch (error) {
    log(`❌ ${name} - 安全测试失败: ${error.message}`, 'red');
    if (error.response) {
      log(`   状态码: ${error.response.status}`, 'red');
      log(`   响应: ${JSON.stringify(error.response.data)}`, 'red');
    }
  }
}

// 1. SQL注入测试
async function testSQLInjection() {
  const maliciousInputs = [
    "'; DROP TABLE writer; --",
    "' OR '1'='1",
    "' UNION SELECT * FROM user --",
    "'; INSERT INTO writer (name) VALUES ('hacked'); --"
  ];

  for (const input of maliciousInputs) {
    try {
      const response = await axios.get(`${BASE_URL}/writers?search=${encodeURIComponent(input)}`);
      
      // 检查是否返回了不应该返回的数据
      if (response.data.writers && response.data.writers.length > 0) {
        // 检查是否包含恶意数据
        const hasMaliciousData = response.data.writers.some(writer => 
          writer.name.includes('hacked') || writer.name.includes('DROP')
        );
        
        if (hasMaliciousData) {
          throw new Error(`SQL注入漏洞检测到: ${input}`);
        }
      }
    } catch (error) {
      if (error.response && error.response.status === 500) {
        throw new Error(`SQL注入可能导致服务器错误: ${input}`);
      }
    }
  }
  
  log('SQL注入测试通过 - 未发现SQL注入漏洞', 'green');
}

// 2. XSS攻击测试
async function testXSSAttack() {
  const xssPayloads = [
    '<script>alert("XSS")</script>',
    '<img src="x" onerror="alert(\'XSS\')">',
    'javascript:alert("XSS")',
    '<svg onload="alert(\'XSS\')">'
  ];

  // 测试作家名称字段
  for (const payload of xssPayloads) {
    try {
      const response = await axios.post(`${BASE_URL}/writers`, {
        name: payload,
        experience: '测试XSS'
      }, {
        headers: {
          'x-auth-token': authToken,
          'Content-Type': 'application/json'
        }
      });
      
      // 检查响应中是否包含未转义的恶意脚本
      const responseText = JSON.stringify(response.data);
      if (responseText.includes('<script>') || responseText.includes('javascript:')) {
        throw new Error(`XSS漏洞检测到: ${payload}`);
      }
    } catch (error) {
      if (error.response && error.response.status === 400) {
        // 400错误是正常的，说明输入被正确验证
        continue;
      }
    }
  }
  
  log('XSS攻击测试通过 - 未发现XSS漏洞', 'green');
}

// 3. 文件上传安全测试
async function testFileUploadSecurity() {
  const maliciousFiles = [
    { name: 'test.php', content: '<?php echo "hacked"; ?>', type: 'application/x-php' },
    { name: 'test.js', content: 'alert("hacked")', type: 'application/javascript' },
    { name: 'test.exe', content: 'fake executable', type: 'application/x-executable' },
    { name: 'test.html', content: '<script>alert("hacked")</script>', type: 'text/html' }
  ];

  for (const file of maliciousFiles) {
    try {
      const FormData = require('form-data');
      const form = new FormData();
      form.append('name', '测试作家');
      form.append('experience', '测试文件上传安全');
      form.append('avatar', Buffer.from(file.content), {
        filename: file.name,
        contentType: file.type
      });

      const response = await axios.post(`${BASE_URL}/writers`, form, {
        headers: {
          'x-auth-token': authToken,
          ...form.getHeaders()
        }
      });
      
      // 如果成功上传了恶意文件，说明存在安全漏洞
      if (response.status === 201) {
        throw new Error(`文件上传安全漏洞: 成功上传了 ${file.name}`);
      }
    } catch (error) {
      if (error.response && error.response.status === 400) {
        // 400错误是正常的，说明文件类型被正确拒绝
        continue;
      }
    }
  }
  
  log('文件上传安全测试通过 - 恶意文件被正确拒绝', 'green');
}

// 4. 认证绕过测试
async function testAuthenticationBypass() {
  const bypassAttempts = [
    { token: '', description: '空token' },
    { token: 'invalid_token', description: '无效token' },
    { token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJ0ZXN0Iiwicm9sZSI6MH0.invalid_signature', description: '无效签名的JWT' },
    { token: 'Bearer invalid', description: '无效Bearer token' }
  ];

  for (const attempt of bypassAttempts) {
    try {
      const response = await axios.post(`${BASE_URL}/writers`, {
        name: '认证绕过测试',
        experience: '测试认证绕过'
      }, {
        headers: {
          'x-auth-token': attempt.token,
          'Content-Type': 'application/json'
        }
      });
      
      // 如果成功访问了需要认证的接口，说明存在认证绕过漏洞
      if (response.status === 201) {
        throw new Error(`认证绕过漏洞: ${attempt.description}`);
      }
    } catch (error) {
      if (error.response && error.response.status === 401) {
        // 401错误是正常的，说明认证被正确拒绝
        continue;
      }
    }
  }
  
  log('认证绕过测试通过 - 未认证访问被正确拒绝', 'green');
}

// 5. 权限提升测试
async function testPrivilegeEscalation() {
  // 首先创建一个普通用户
  const normalUser = {
    userId: 'normaluser123',
    username: '普通用户',
    password: 'normalpass123'
  };

  try {
    // 注册普通用户
    await axios.post(`${BASE_URL}/register`, normalUser);
    
    // 登录普通用户
    const loginResponse = await axios.post(`${BASE_URL}/login`, {
      userId: normalUser.userId,
      password: normalUser.password
    });
    
    const normalUserToken = loginResponse.data.token;
    
    // 尝试访问管理员功能
    try {
      const response = await axios.get(`${BASE_URL}/user/profile`, {
        headers: {
          'x-auth-token': normalUserToken
        }
      });
      
      // 检查用户角色
      if (response.data.role === 1) {
        throw new Error('权限提升漏洞: 普通用户获得了管理员权限');
      }
    } catch (error) {
      if (error.response && error.response.status === 403) {
        log('权限提升测试通过 - 普通用户无法访问管理员功能', 'green');
      }
    }
  } catch (error) {
    log('权限提升测试通过 - 权限控制正常工作', 'green');
  }
}

// 6. 会话管理测试
async function testSessionManagement() {
  // 测试JWT过期时间
  const loginResponse = await axios.post(`${BASE_URL}/login`, {
    userId: 'testuser123',
    password: 'testpass123'
  });
  
  const token = loginResponse.data.token;
  
  // 解码JWT token检查过期时间
  const jwt = require('jsonwebtoken');
  const decoded = jwt.decode(token);
  
  if (decoded && decoded.exp) {
    const expirationTime = new Date(decoded.exp * 1000);
    const currentTime = new Date();
    const timeDifference = expirationTime - currentTime;
    
    // 检查token是否在合理时间内过期（比如24小时）
    if (timeDifference > 24 * 60 * 60 * 1000) {
      log('警告: JWT token过期时间可能过长', 'yellow');
    } else {
      log('会话管理测试通过 - JWT过期时间合理', 'green');
    }
  }
}

// 7. 输入验证测试
async function testInputValidation() {
  const maliciousInputs = [
    { field: 'name', value: 'a'.repeat(1000), description: '超长输入' },
    { field: 'name', value: '   ', description: '空白输入' },
    { field: 'name', value: 'test<script>alert("xss")</script>', description: '包含脚本的输入' },
    { field: 'experience', value: 'a'.repeat(10000), description: '超长文本输入' }
  ];

  for (const input of maliciousInputs) {
    try {
      const requestData = {
        name: input.field === 'name' ? input.value : '测试作家',
        experience: input.field === 'experience' ? input.value : '测试生平'
      };

      const response = await axios.post(`${BASE_URL}/writers`, requestData, {
        headers: {
          'x-auth-token': authToken,
          'Content-Type': 'application/json'
        }
      });
      
      // 检查响应中是否包含恶意内容
      const responseText = JSON.stringify(response.data);
      if (responseText.includes('<script>') || responseText.length > 10000) {
        throw new Error(`输入验证漏洞: ${input.description}`);
      }
    } catch (error) {
      if (error.response && error.response.status === 400) {
        // 400错误是正常的，说明输入被正确验证
        continue;
      }
    }
  }
  
  log('输入验证测试通过 - 恶意输入被正确验证', 'green');
}

// 8. 敏感信息泄露测试
async function testInformationDisclosure() {
  const sensitiveEndpoints = [
    '/api/config',
    '/api/debug',
    '/api/error',
    '/api/version',
    '/api/admin'
  ];

  for (const endpoint of sensitiveEndpoints) {
    try {
      const response = await axios.get(`${BASE_URL}${endpoint}`);
      
      // 检查是否泄露了敏感信息
      const responseText = JSON.stringify(response.data);
      const sensitivePatterns = [
        /password/i,
        /secret/i,
        /key/i,
        /token/i,
        /database/i,
        /connection/i
      ];
      
      for (const pattern of sensitivePatterns) {
        if (pattern.test(responseText)) {
          throw new Error(`敏感信息泄露: ${endpoint} 可能包含敏感信息`);
        }
      }
    } catch (error) {
      if (error.response && (error.response.status === 404 || error.response.status === 403)) {
        // 404或403错误是正常的，说明端点不存在或需要权限
        continue;
      }
    }
  }
  
  log('敏感信息泄露测试通过 - 未发现敏感信息泄露', 'green');
}

// 9. 错误处理测试
async function testErrorHandling() {
  const errorTests = [
    { method: 'get', url: '/api/writers/999999', description: '不存在的资源' },
    { method: 'post', url: '/api/writers', data: {}, description: '无效的请求数据' },
    { method: 'put', url: '/api/writers/999999', data: {}, description: '更新不存在的资源' }
  ];

  for (const test of errorTests) {
    try {
      let response;
      if (test.method === 'get') {
        response = await axios.get(`${BASE_URL}${test.url}`);
      } else if (test.method === 'post') {
        response = await axios.post(`${BASE_URL}${test.url}`, test.data, {
          headers: {
            'x-auth-token': authToken,
            'Content-Type': 'application/json'
          }
        });
      } else if (test.method === 'put') {
        response = await axios.put(`${BASE_URL}${test.url}`, test.data, {
          headers: {
            'x-auth-token': authToken,
            'Content-Type': 'application/json'
          }
        });
      }
      
      // 检查错误响应是否包含敏感信息
      const responseText = JSON.stringify(response.data);
      if (responseText.includes('stack trace') || responseText.includes('error details')) {
        throw new Error(`错误处理漏洞: ${test.description} 可能泄露敏感信息`);
      }
    } catch (error) {
      if (error.response) {
        const responseText = JSON.stringify(error.response.data);
        if (responseText.includes('stack trace') || responseText.includes('error details')) {
          throw new Error(`错误处理漏洞: ${test.description} 泄露了错误详情`);
        }
      }
    }
  }
  
  log('错误处理测试通过 - 错误信息处理得当', 'green');
}

// 10. 速率限制测试
async function testRateLimiting() {
  const requests = [];
  
  // 发送多个快速请求
  for (let i = 0; i < 20; i++) {
    requests.push(
      axios.get(`${BASE_URL}/writers`).catch(error => error)
    );
  }
  
  try {
    const responses = await Promise.all(requests);
    
    // 检查是否有请求被限制
    const limitedRequests = responses.filter(response => 
      response.response && response.response.status === 429
    );
    
    if (limitedRequests.length === 0) {
      log('警告: 未检测到速率限制机制', 'yellow');
    } else {
      log('速率限制测试通过 - 检测到速率限制机制', 'green');
    }
  } catch (error) {
    log('速率限制测试通过 - 系统正常运行', 'green');
  }
}

// 主测试函数
async function runSecurityTests() {
  log('开始安全测试...', 'yellow');
  
  // 首先登录获取token
  try {
    const loginResponse = await axios.post(`${BASE_URL}/login`, {
      userId: 'testuser123',
      password: 'testpass123'
    });
    authToken = loginResponse.data.token;
    log('获取测试token成功', 'green');
  } catch (error) {
    log('无法获取测试token，部分测试可能失败', 'yellow');
  }
  
  // 执行安全测试
  await testSecurity('SQL注入测试', testSQLInjection);
  await testSecurity('XSS攻击测试', testXSSAttack);
  await testSecurity('文件上传安全测试', testFileUploadSecurity);
  await testSecurity('认证绕过测试', testAuthenticationBypass);
  await testSecurity('权限提升测试', testPrivilegeEscalation);
  await testSecurity('会话管理测试', testSessionManagement);
  await testSecurity('输入验证测试', testInputValidation);
  await testSecurity('敏感信息泄露测试', testInformationDisclosure);
  await testSecurity('错误处理测试', testErrorHandling);
  await testSecurity('速率限制测试', testRateLimiting);
  
  log('\n🔒 所有安全测试完成！', 'green');
}

// 运行安全测试
runSecurityTests().catch(error => {
  log(`安全测试过程中发生错误: ${error.message}`, 'red');
  process.exit(1);
}); 